package org.alwayssuper.netmc.handler;

import org.alwayssuper.netmc.codec.Delimiter;
import org.alwayssuper.netmc.util.ByteBufUtils;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.TooLongFrameException;
import io.netty.util.internal.ObjectUtil;

import java.util.List;

import static io.netty.util.internal.ObjectUtil.checkPositive;

/**
 * 基于头部分隔符和长度字段的帧解码器
 * 该解码器首先查找头部分隔符，然后根据指定位置的长度字段确定报文长度
 * 
 * @author alwayssuper
 */
public class LengthFieldBasedHeaderDelimiterDecoder extends ByteToMessageDecoder {

    private final Delimiter[] delimiters;
    private final int maxFrameLength;
    private final boolean failFast;
    private final int headerLength;
    private final int bodyLengthFieldStartPos;
    private final int bodyLengthFieldEndPos;
    private boolean discardingTooLongFrame;
    private int tooLongFrameLength;

    /**
     * 构造函数
     * 
     * @param maxFrameLength 最大帧长度
     * @param headerLength 报文头长度
     * @param bodyLengthFieldStartPos 报文体长度字段起始位置（相对于分隔符后的位置）
     * @param bodyLengthFieldEndPos 报文体长度字段结束位置（相对于分隔符后的位置）
     * @param delimiters 分隔符数组
     */
    public LengthFieldBasedHeaderDelimiterDecoder(int maxFrameLength, int headerLength, 
            int bodyLengthFieldStartPos, int bodyLengthFieldEndPos, Delimiter... delimiters) {
        this(maxFrameLength, headerLength, bodyLengthFieldStartPos, bodyLengthFieldEndPos, true, delimiters);
    }

    /**
     * 构造函数
     * 
     * @param maxFrameLength 最大帧长度
     * @param headerLength 报文头长度
     * @param bodyLengthFieldStartPos 报文体长度字段起始位置（相对于分隔符后的位置）
     * @param bodyLengthFieldEndPos 报文体长度字段结束位置（相对于分隔符后的位置）
     * @param failFast 是否快速失败
     * @param delimiters 分隔符数组
     */
    public LengthFieldBasedHeaderDelimiterDecoder(int maxFrameLength, int headerLength, 
            int bodyLengthFieldStartPos, int bodyLengthFieldEndPos, boolean failFast, Delimiter... delimiters) {
        validateMaxFrameLength(maxFrameLength);
        ObjectUtil.checkNonEmpty(delimiters, "delimiters");
        
        // 验证新参数
        checkPositive(headerLength, "headerLength");
        if (bodyLengthFieldStartPos < 0) {
            throw new IllegalArgumentException("bodyLengthFieldStartPos must be non-negative: " + bodyLengthFieldStartPos);
        }
        if (bodyLengthFieldEndPos <= bodyLengthFieldStartPos) {
            throw new IllegalArgumentException("bodyLengthFieldEndPos must be greater than bodyLengthFieldStartPos: " 
                    + bodyLengthFieldStartPos + ", " + bodyLengthFieldEndPos);
        }

        this.delimiters = delimiters;
        this.maxFrameLength = maxFrameLength;
        this.failFast = failFast;
        this.headerLength = headerLength;
        this.bodyLengthFieldStartPos = bodyLengthFieldStartPos;
        this.bodyLengthFieldEndPos = bodyLengthFieldEndPos;
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        Object decoded = decode(ctx, in);
        if (decoded != null) {
            out.add(decoded);
        }
    }

    protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
        // 尝试所有分隔符并选择产生最短帧的分隔符
        int minFrameLength = Integer.MAX_VALUE;
        Delimiter minDelim = null;
        int actualFrameLength = 0;
        
        for (Delimiter delim : delimiters) {
            int frameLength = ByteBufUtils.indexOf(buffer, delim.value);
            if (frameLength >= 0 && frameLength < minFrameLength) {
                minFrameLength = frameLength;
                minDelim = delim;
                actualFrameLength = frameLength;
            }
        }

        if (minDelim != null) {
            // 跳过分隔符前的无效字节
            if (actualFrameLength > 0) {
                buffer.skipBytes(actualFrameLength);
            }
            // GBT32960协议只有头部分隔符，后续长度判断由剩余报文头动态决定
            return decodeWithDelimiter(buffer, minDelim);
        } else {
            // 未找到分隔符
            handleNoDelimiterFound(buffer);
            return null;
        }
    }

    private Object decodeWithDelimiter(ByteBuf buffer, Delimiter minDelim) {
        int minDelimLength = minDelim.value.length;

        if (discardingTooLongFrame) {
            // 刚刚完成丢弃一个非常大的帧
            // 回到初始状态
            discardingTooLongFrame = false;
            buffer.skipBytes( minDelimLength);

            int tooLongFrameLength = this.tooLongFrameLength;
            this.tooLongFrameLength = 0;
            if (!failFast) {
                fail(tooLongFrameLength);
            }
            return null;
        }

        // 计算报文总长度
        int totalFrameLength = calculateFrameLength(buffer, minDelim);
        
        // 如果无法计算长度，返回null
        if (totalFrameLength == -1) {
            return null;
        }

        // 检查是否需要包含分隔符长度
        int frameLengthWithDelimiter = minDelim.strip ? totalFrameLength : totalFrameLength + minDelimLength;
        
        if (frameLengthWithDelimiter > maxFrameLength) {
            // 丢弃读取的分隔符帧，后续的无效字节由decode方法中的 buffer.skipBytes(actualFrameLength)丢弃
            buffer.skipBytes(minDelimLength);
            fail(frameLengthWithDelimiter);
            return null;
        }

        // 对于头部分隔符，我们从缓冲区的开始读取到计算出的帧长度
        if (minDelim.strip) {
            return decodeWithStrip(buffer, minDelimLength, totalFrameLength);
        } else {
            return decodeWithoutStrip(buffer, minDelimLength, totalFrameLength);
        }
    }

    /**
     * 根据分隔符后指定位置的长度字段计算帧总长度
     * 
     * @param buffer 缓冲区
     * @param minDelim 最短分隔符
     * @return 帧总长度
     */
    private int calculateFrameLength(ByteBuf buffer, Delimiter minDelim) {
        // 分隔符后的位置
        int startPos = minDelim.value.length;
        
        // 检查是否有足够的数据读取长度字段
        if (buffer.readableBytes() < startPos + bodyLengthFieldEndPos) {
            // 数据不足，无法计算长度
            return -1;
        }
        
        // 读取长度字段，从bodyLengthFieldStartPos到bodyLengthFieldEndPos之间的字节流（包含bodyLengthFieldEndPos的字节）转换为无符号数值
        int lengthFieldLength = bodyLengthFieldEndPos - bodyLengthFieldStartPos;
        int bodyLength = 0;
        for (int i = 0; i < lengthFieldLength; i++) {
            bodyLength = (bodyLength << 8) | (buffer.getUnsignedByte(buffer.readerIndex() + startPos + bodyLengthFieldStartPos + i) & 0xFF);
        }
        
        // 返回总长度：报文头长度 + 报文体长度 + 效验字段长度
        return headerLength + bodyLength + 1;
    }

    private ByteBuf decodeWithStrip(ByteBuf buffer, int minDelimLength, int totalFrameLength) {
        // 忽略长度为0的帧
        if (totalFrameLength != 0) {
            // 跳过分隔符
            buffer.skipBytes(minDelimLength);
            // 读取帧数据，只读取报文头和报文体部分
            return buffer.readRetainedSlice(totalFrameLength);
        }
        
        return null;
    }

    private ByteBuf decodeWithoutStrip(ByteBuf buffer, int minDelimLength, int totalFrameLength) {
        // 在帧中包含分隔符
        if (totalFrameLength != 0) {
            // 总长度需要包含分隔符长度
            return buffer.readRetainedSlice(totalFrameLength + minDelimLength);
        }
        
        return null;
    }

    private void handleNoDelimiterFound(ByteBuf buffer) {
        if (!discardingTooLongFrame) {
            if (buffer.readableBytes() > maxFrameLength) {
                // 丢弃缓冲区的内容
                tooLongFrameLength = buffer.readableBytes();
                buffer.skipBytes(buffer.readableBytes());
                discardingTooLongFrame = true;
                if (failFast) {
                    fail(tooLongFrameLength);
                }
            }
        } else {
            // 由于未找到分隔符，仍在丢弃缓冲区
            tooLongFrameLength += buffer.readableBytes();
            buffer.skipBytes(buffer.readableBytes());
        }
    }

    private void fail(long frameLength) {
        if (frameLength > 0) {
            throw new TooLongFrameException("frame length exceeds " + maxFrameLength + ": " + frameLength + " - discarded");
        } else {
            throw new TooLongFrameException("frame length exceeds " + maxFrameLength + " - discarding");
        }
    }

    private static void validateMaxFrameLength(int maxFrameLength) {
        checkPositive(maxFrameLength, "maxFrameLength");
    }
}